package P3;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.BufferUnderflowException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.Vector;

import NIOCommunication.NIOProcessor;
import NIOCommunication.NIOProcessorCloseContextCommand;
import NIOCommunication.NIOSocketContext;

public class CoordinationContext extends NIOSocketContext
{
	private String mPartialReply = "";
	private int mBytesRemaining = 0;  // bytes left in the current message

	public CoordinationContext(NIOProcessor processor, SocketChannel socketChannel)
	{
		super(processor, socketChannel);
		mPartialReply = "";
		mBytesRemaining = 0;
	}
	
	public CoordinationContext(NIOProcessor processor) throws UnknownHostException
	{
		super(processor);
		mPartialReply = "";
		mBytesRemaining = 0;
	}	
	
	public CoordinationContext(NIOProcessor processor, InetSocketAddress address)
	{
		super(processor, address);
		mPartialReply = "";
		mBytesRemaining = 0;
	}
	
	@Override
	public void OnReadable(SelectionKey key)
	{
		//System.out.println("Reading=========================================");
		try
		{
			int readLength = 0;
			if( (readLength = mSocketChannel.read(mReadBuffer)) != -1)
			{
				//System.out.println("read " + readLength + ". buf = " + mReadBuffer);
				mReadBuffer.flip();

                                while(mReadBuffer.hasRemaining())
                                {
                                    if(!InnerRead())
                                    {
                                        mReadBuffer.compact();
                                        return;
                                    }
                                }
                                
                                mReadBuffer.clear();
                        }
			else
			{
				System.out.println("read end-of-file.  Requesting socket close.");
				mNIOProcessor.QueueCommand(new NIOProcessorCloseContextCommand(this));
			}
		}
		catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
			mNIOProcessor.QueueCommand(new NIOProcessorCloseContextCommand(this));
		}
	}
        
        private boolean InnerRead()
        {
            if(mBytesRemaining == 0)
            {
                try
                {
                    byte[] lengthBytes = new byte[4];
                                            
                    mReadBuffer.get(lengthBytes);
                    mBytesRemaining = BytesToInt(lengthBytes);
                    //System.out.println("Message Length: " + mBytesRemaining);
                }
                catch(BufferUnderflowException e)
                {
                    // still waiting to read 4 bytes for the length.
                    //System.out.println("Waiting to read length...");
                    return false;
                }
            }
				
            int remaining = mReadBuffer.remaining();
            int bytesToRead = Math.min(remaining, mBytesRemaining); // force a break on end of message
            byte[] bytes = new byte[bytesToRead];
            mReadBuffer.get(bytes);
            mBytesRemaining -= bytesToRead;
            OnReadData(bytes);
            //System.out.println("Read " + bytesToRead + " bytes.  Still have " + mBytesRemaining + " bytes to go.");
            
            return true;
        }
	
	@Override
	public void OnReadData(byte[] data)
	{
		mPartialReply = mPartialReply + new String(data);
		
		
		if(mBytesRemaining == 0)
		{
			OnNewMessage(mPartialReply);
			mPartialReply = "";
		}
	}

	public void SendMessage(String message)
	{
		byte[] messageBytes = message.getBytes();
		WriteData(IntToBytes(messageBytes.length));
                WriteData(messageBytes);
	}
	
	public void OnNewMessage(String message)
	{
	}
	
	private static byte[] IntToBytes(int in)
	{
		byte[] bytes = new byte[4];

		bytes[3] = (byte)((in      ) - 128);
		bytes[2] = (byte)((in >> 8 ) - 128);
		bytes[1] = (byte)((in >> 16) - 128);
		bytes[0] = (byte)((in >> 24) - 128);
		
		return bytes;
	}
	
	private static int BytesToInt(byte[] bytes)
	{
		int out = 0;

		out += (((int)bytes[3]) + 128);
		out += (((int)bytes[2]) + 128) << 8;
		out += (((int)bytes[1]) + 128) << 16;
		out += (((int)bytes[0]) + 128) << 24;
		
		return out;
	}

	//@Override
	//public void Close()
	//{
	//	super.Close();
	//}
}
